package com.zsn.demo.test.lock;

import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author zsn
 * @CreateTime: 2022/12/6 23:53
 * @Description:
 */
public class LockTest2 {

    public static void main(String[] args) {
        IntLock2 intLock1 = new IntLock2(22);
        IntLock2 intLock2 = new IntLock2(11);

        Thread thread1 = new Thread(intLock1);
        Thread thread2 = new Thread(intLock2);
        thread1.start();
        thread2.start();
        if (thread2.isAlive()) {
            thread2.interrupt();
        }
    }

    static class IntLock implements Runnable {

        private static ReentrantLock lock1 = new ReentrantLock();
        private static ReentrantLock lock2 = new ReentrantLock();

        //用于控制锁的顺序
        private int lockNum;

        public IntLock(int lockNum) {
            this.lockNum = lockNum;
        }

        @Override
        public void run() {
            // 偶数先锁一再锁二
            if (lockNum % 2 == 0) {
                while (true) {
                    try {
                        if (lock1.tryLock()) {
                            System.out.println(Thread.currentThread().getName() + "获得锁一，还想获得锁二");
                            Thread.sleep(100);
                            try {
                                if (lock2.tryLock()) {
                                    System.out.println(Thread.currentThread().getName() + "获得锁二，还想获得锁一");
                                    return;
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                            } finally {
                                if (lock2.isHeldByCurrentThread()) {
                                    lock2.unlock();
                                }
                            }
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        if (lock1.isHeldByCurrentThread()) {
                            lock1.unlock();
                        }
                    }
                }
            } else {
                // 奇数先获得锁二再获得锁一
                while (true) {
                    try {
                        if (lock2.tryLock()) {
                            System.out.println(Thread.currentThread().getName() + "获得锁二，还想获得锁一");
                            Thread.sleep(100);
                            try {
                                if (lock1.tryLock()) {
                                    System.out.println(Thread.currentThread().getName() + "获得锁一，还想获得锁二");
                                    return;
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                            } finally {
                                if (lock1.isHeldByCurrentThread()) {
                                    lock1.unlock();
                                }
                            }
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        if (lock2.isHeldByCurrentThread()) {
                            lock2.unlock();
                        }
                    }
                }

            }
        }
    }

    static class IntLock2 implements Runnable {

        private static ReentrantLock lock1 = new ReentrantLock();
        private static ReentrantLock lock2 = new ReentrantLock();

        //用于控制锁的顺序
        private int lockNum;

        public IntLock2(int lockNum) {
            this.lockNum = lockNum;
        }

        @Override
        public void run() {
            try {
                // 偶数先锁一再锁二
                if (lockNum % 2 == 1) {
                    lock1.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() + "获得锁 1,还需 要获得锁 2");
                    Thread.sleep(new Random().nextInt(500));
                    lock2.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() + "获得锁 1,又获得锁 2");
                } else {
                    // 奇数先获得锁二再获得锁一
                    lock2.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() + "获得锁 2,还需 要获得锁 1");
                    Thread.sleep(new Random().nextInt(500));
                    lock1.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() + "获得锁 2,又获得锁 1");
                }
            } catch (Exception e) {

            } finally {
                if (lock1.isHeldByCurrentThread()) {
                    lock1.unlock();
                }
                if (lock2.isHeldByCurrentThread()) {
                    lock2.unlock();
                }
                System.out.println(Thread.currentThread().getName() + "线程退出");
            }

        }
    }
}


/**
 * Lock 锁的基本使用
 */
class Test02 {
    //定义显示锁
    static Lock lock = new ReentrantLock();

    //定义方法
    public static void sm() {
        //先获得锁
        lock.lock();
        //for 循环就是同步代码块
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + " -- " + i);
        }
        //释放锁
        lock.unlock();
    }

    public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                sm();
            }
        };
        //启动三个线程
        new Thread(r).start();
        new Thread(r).start();
        new Thread(r).start();
    }
}

/**
 * 使用 Lock 锁同步不同方法中的同步代码块
 */
class Test03 {
    static Lock lock = new ReentrantLock(); //定义锁对象

    public static void sm1() {
        //经常在 try 代码块中获得 Lock 锁, 在 finally 子句中释放锁
        try {
            lock.lock(); //获得锁
            System.out.println(Thread.currentThread().getName() + "-- method 1 -- " +
                    System.currentTimeMillis());
            Thread.sleep(new Random().nextInt(1000));
            System.out.println(Thread.currentThread().getName() + "-- method 1 -- " +
                    System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock(); //释放锁
        }
    }

    public static void sm2() {
        try {
            lock.lock(); //获得锁
            System.out.println(Thread.currentThread().getName() + "-- method 22 -- " +
                    System.currentTimeMillis());
            Thread.sleep(new Random().nextInt(1000));
            System.out.println(Thread.currentThread().getName() + "-- method 22 -- " +
                    System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock(); //释放锁
        }
    }

    public static void main(String[] args) {
        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                sm1();
            }
        };
        Runnable r2 = new Runnable() {
            @Override
            public void run() {
                sm2();
            }
        };
        new Thread(r1).start();
        new Thread(r1).start();
        new Thread(r1).start();
        new Thread(r2).start();
        new Thread(r2).start();
        new Thread(r2).start();
    }
}

/**
 * ReentrantLock 锁的可重入性
 */
class Test04 {
    static class Subthread extends Thread {
        private static Lock lock = new ReentrantLock(); //定义锁对象
        public static int num = 0; //定义变量

        @Override
        public void run() {
            for (int i = 0; i < 10000; i++) {
                try {
                    //可重入锁指可以反复获得该锁
                    lock.lock();
                    lock.lock();
                    num++;
                } finally {
                    lock.unlock();
                    lock.unlock();
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Subthread t1 = new Subthread();
        Subthread t2 = new Subthread();
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(Subthread.num);
    }
}


/**
 * lockInterruptibly() 方法的作用:如果当前线程未被中断则获得锁,如果当前线程被中断则出现异常.
 */
class Test05 {
    static class Servier {
        private Lock lock = new ReentrantLock(); //定义锁对象

        public void serviceMethod() {
            try {
                // lock.lock(); //获得锁定,即使调用了线程的 interrupt()方法,也没有真正的中断线程
                lock.lockInterruptibly(); //如果线程被中断了,不会获得锁,会产生异常
                System.out.println(Thread.currentThread().getName() + "-- begin lock");
                //执行一段耗时的操作
                for (int i = 0; i < Integer.MAX_VALUE; i++) {
                    new StringBuilder();
                }
                System.out.println(Thread.currentThread().getName() + " -- end lock");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println(Thread.currentThread().getName() + " ***** 释放锁");
                lock.unlock(); //释放锁
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Servier s = new Servier();
        Runnable r = new Runnable() {
            @Override
            public void run() {
                s.serviceMethod();
            }
        };
        Thread t1 = new Thread(r);
        t1.start();
        Thread.sleep(50);
        Thread t2 = new Thread(r);
        t2.start();
        Thread.sleep(50);
        t2.interrupt(); //中断 t2 线程
    }
}


/**
 * 通过 ReentrantLock 锁的 lockInterruptibly()方法避免死锁的产生
 */
class Test06 {
    static class IntLock implements Runnable {
        //创建两个 ReentrantLock 锁对象
        public static ReentrantLock lock1 = new ReentrantLock();
        public static ReentrantLock lock2 = new ReentrantLock();
        int lockNum; //定义整数变量,决定使用哪个锁

        public IntLock(int lockNum) {
            this.lockNum = lockNum;
        }

        @Override
        public void run() {
            try {
                if (lockNum % 2 == 1) { //奇数,先锁 1,再锁 2
                    lock1.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() + "获得锁 1,还需 要获得锁 2");
                    Thread.sleep(new Random().nextInt(500));
                    lock2.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() + "同时获得了锁 1 与锁 2....");
                } else { //偶数,先锁 2,再锁 1
                    lock2.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() + "获得锁 2,还需 要获得锁 1");
                    Thread.sleep(new Random().nextInt(500));
                    lock1.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() + "同时获得了锁 1 与锁 2....");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (lock1.isHeldByCurrentThread()) //判断当前线程是否持有该锁
                    lock1.unlock();
                if (lock2.isHeldByCurrentThread())
                    lock2.unlock();
                System.out.println(Thread.currentThread().getName() + "线程退出");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        IntLock intLock1 = new IntLock(11);
        IntLock intLock2 = new IntLock(22);
        Thread t1 = new Thread(intLock1);
        Thread t2 = new Thread(intLock2);
        t1.start();
        t2.start();
        //在 main 线程,等待 3000 秒,如果还有线程没有结束就中断该线程
        Thread.sleep(3000);
        //可以中断任何一个线程来解决死锁, t2 线程会放弃对锁 1 的申请,同时释放锁 2, t1 线程会完成它的任务
        if (t2.isAlive()) {
            t2.interrupt();
        }
    }
}

/**
 * tryLock(long time, TimeUnit unit) 的基本使用
 */
class Test07 {
    static class TimeLock implements Runnable {
        private static ReentrantLock lock = new ReentrantLock(); //定义锁对象

        @Override
        public void run() {
            try {
                if (lock.tryLock(3, TimeUnit.SECONDS)) { //获得锁返回 true
                    System.out.println(Thread.currentThread().getName() + "获得锁,执行耗 时任务");
                    Thread.sleep(4000); //假设 Thread-0 线程先持有锁,完成任务需要 4 秒钟,Thread-1 线程尝试获得锁,Thread-1 线程在 3 秒内还没有获得锁的话,Thread-1线程会放弃
//                    Thread.sleep(2000); //假设 Thread-0 线程先持有锁,完成任务需要 2 秒钟,Thread-1 线程尝试获得锁,Thread-1 线程会一直尝试,在它约定尝试的 3 秒内可以获得锁对象
                } else { //没有获得锁
                    System.out.println(Thread.currentThread().getName() + "没有获得锁");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }
    }

    public static void main(String[] args) {
        TimeLock timeLock = new TimeLock();
        Thread t1 = new Thread(timeLock);
        Thread t2 = new Thread(timeLock);
        t1.start();
        t2.start();
    }
}

/**
 * tryLock() * 当锁对象没有被其他线程持有的情况下才会获得该锁定
 */
class Test08 {
    static class Service {
        private ReentrantLock lock = new ReentrantLock();

        public void serviceMethod() {
            try {
                if (lock.tryLock()) {
                    System.out.println(Thread.currentThread().getName() + "获得锁定");
                    Thread.sleep(3000); //模拟执行任务的时长
                } else {
                    System.out.println(Thread.currentThread().getName() + "没有获得锁定");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Service service = new Service();
        Runnable r = new Runnable() {
            @Override
            public void run() {
                service.serviceMethod();
            }
        };
        Thread t1 = new Thread(r);
        t1.start();
        Thread.sleep(50); //睡眠 50 毫秒,确保 t1 线程锁定
        Thread t2 = new Thread(r);
        t2.start();
    }
}


/**
 * 使用 tryLock()可以避免死锁
 */
class Test09 {
    static class IntLock implements Runnable {
        private static ReentrantLock lock1 = new ReentrantLock();
        private static ReentrantLock lock2 = new ReentrantLock();
        private int lockNum; //用于控制锁的顺序

        public IntLock(int lockNum) {
            this.lockNum = lockNum;
        }

        @Override
        public void run() {
            if (lockNum % 2 == 0) { //偶数先锁 1,再锁 2
                while (true) {
                    try {
                        if (lock1.tryLock()) {
                            System.out.println(Thread.currentThread().getName() + "获得 锁 1, 还想获得锁 2");
                            Thread.sleep(new Random().nextInt(100));
                            try {
                                if (lock2.tryLock()) {
                                    System.out.println(Thread.currentThread().getName() + "同时获得锁 1 与锁 2 ----完成任务了");
                                    return; //结束 run()方法执行,即当前线程结束
                                }
                            } finally {
                                if (lock2.isHeldByCurrentThread()) {
                                    lock2.unlock();
                                }
                            }
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        if (lock1.isHeldByCurrentThread()) {
                            lock1.unlock();
                        }
                    }
                }
            } else { //奇数就先锁 2,再锁 1
                while (true) {
                    try {
                        if (lock2.tryLock()) {
                            System.out.println(Thread.currentThread().getName() + "获得 锁 2, 还想获得锁 1");
                            Thread.sleep(new Random().nextInt(100));
                            try {
                                if (lock1.tryLock()) {
                                    System.out.println(Thread.currentThread().getName() + "同时获得锁 1 与锁 2 ----完成任务了");
                                    return; //结束 run()方法执行,即当前线程结束
                                }
                            } finally {
                                if (lock1.isHeldByCurrentThread()) {
                                    lock1.unlock();
                                }
                            }
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        if (lock2.isHeldByCurrentThread()) {

//                            lock2.unlock();
//                            try {
//                                Thread.sleep(1000);
//                            } catch (InterruptedException e) {
//                                e.printStackTrace();
//                            }
                        }
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        IntLock intLock1 = new IntLock(11);
        IntLock intLock2 = new IntLock(22);
        Thread t1 = new Thread(intLock1);
        Thread t2 = new Thread(intLock2);
        t1.start();
        t2.start();
        //运行后,使用 tryLock()尝试获得锁,不会傻傻的等待,通过循环不停的再次尝试,如果等待的时间足够长,线程总是会获得想要的资源
    }
}


/**
 * Condition 等待与通知
 */
class Test001 {
    //定义锁
    static Lock lock = new ReentrantLock();
    //获得 Condtion 对象
    static Condition condition = lock.newCondition();

    //定义线程子类
    static class SubThread extends Thread {
        @Override
        public void run() {
            try {
                lock.lock(); //在调用 await()前必须先获得锁
                System.out.println("method lock");
                condition.await(); //等待
                System.out.println("method await");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock(); //释放锁
                System.out.println("method unlock");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SubThread t = new SubThread();
        t.start();
        //子线程启动后,会转入等待状态
        Thread.sleep(3000);
        //主线程在睡眠 3 秒后,唤醒子线程的等待
        try {
            lock.lock();
            condition.signal();
        } finally {
            lock.unlock();
        }
    }
}

/**
 * 多个 Condition 实现通知部分线程, 使用更灵活
 */
class Test002 {
    static class Service {
        private ReentrantLock lock = new ReentrantLock(); //定义锁对象
        //定义两个 Condtion 对象
        private Condition conditionA = lock.newCondition();
        private Condition conditionB = lock.newCondition();

        //定义方法,使用 conditionA 等待
        public void waitMethodA() {
            try {
                lock.lock();
                System.out.println(Thread.currentThread().getName() + " begin wait:" +
                        System.currentTimeMillis());
                conditionA.await(); //等待
                System.out.println(Thread.currentThread().getName() + " end wait:" +
                        System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

        //定义方法,使用 conditionB 等待
        public void waitMethodB() {
            try {
                lock.lock();
                System.out.println(Thread.currentThread().getName() + " begin wait:" +
                        System.currentTimeMillis());
                conditionB.await(); //等待
                System.out.println(Thread.currentThread().getName() + " end wait:" +
                        System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

        //定义方法唤醒 conditionA 对象上的等待
        public void signalA() {
            try {
                lock.lock();
                System.out.println(Thread.currentThread().getName() + " sigal A time = " +
                        System.currentTimeMillis());
                conditionA.signal();
                System.out.println(Thread.currentThread().getName() + " sigal A time = " +
                        System.currentTimeMillis());
            } finally {
                lock.unlock();
            }
        }

        //定义方法唤醒 conditionB 对象上的等待
        public void signalB() {
            try {
                lock.lock();
                System.out.println(Thread.currentThread().getName() + " sigal B time = " +
                        System.currentTimeMillis());
                conditionB.signal();
                System.out.println(Thread.currentThread().getName() + " sigal B time = " +
                        System.currentTimeMillis());
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Service service = new Service();
        //开启两个线程,分别调用 waitMethodA(),waitMethodB()方法
        new Thread(new Runnable() {
            @Override
            public void run() {
                service.waitMethodA();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                service.waitMethodB();
            }
        }).start();
        Thread.sleep(3000); //main 线程睡眠 3 秒
        // service.signalA(); //唤醒 conditionA 对象上的等待,conditionB 上的等待依然继续等待
        service.signalB();
    }
}

/**
 * 使用 Condition 实现生产者/消费者设计模式, 两个 线程交替打印
 */
class Test003 {
    static class MyService {
        private Lock lock = new ReentrantLock(); //创建锁对象
        private Condition condition = lock.newCondition(); //创建 Condition 对象
        private boolean flag = true; //定义交替打印标志

        //定义方法只打印----横线
        public void printOne() {
            try {
                lock.lock(); //锁定
                while (flag) { //当 flag 为 true 等待
                    System.out.println(Thread.currentThread().getName() + " waiting...");
                    condition.await();
                }
                //flag 为 false 时打印
                System.out.println(Thread.currentThread().getName() + " ---------------- ");
                flag = true; //修改交替打印标志
                condition.signal(); //通知另外的线程打印
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock(); //释放锁对象
            }
        }

        //定义方法只打印***横线
        public void printTwo() {
            try {
                lock.lock(); //锁定
                while (!flag) { //当 flag 为 false 等待
                    System.out.println(Thread.currentThread().getName() + " waiting...");
                    condition.await();
                }
                //flag 为 true 时打印
                System.out.println(Thread.currentThread().getName() + " ****** ");
                flag = false; //修改交替打印标志
                condition.signal(); //通知另外的线程打印
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock(); //释放锁对象
            }
        }
    }

    public static void main(String[] args) {
        MyService myService = new MyService();
        //创建线程打印--
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    myService.printOne();
                }
            }
        }).start();

        //创建线程打印**
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    myService.printTwo();
                }
            }
        }).start();
    }
}


/**
 * 公平 锁与非公平锁
 */
class Test0001 {
     static ReentrantLock lock = new ReentrantLock(); //默认是非公平锁
//    static ReentrantLock lock = new ReentrantLock(true); //定义公平锁
    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                while (true){
                    try {
                        lock.lock();
                        System.out.println(Thread.currentThread().getName() + " 获 得 了 锁对象");
                    }finally {
                        lock.unlock();
                    }
                }
            }
        };
        for (int i = 0; i < 5; i++) {
            new Thread(runnable).start();
        }
        /* 运行程序
        1)如果是非公平锁, 系统倾向于让一个线程再次获得已经持有的锁, 这种
        分配策略是高效的,非公平的
        2)如果是公平锁, 多个线程不会发生同一个线程连续多次获得锁的可能, 保证了公平性
        */
    }
}
